#!/usr/bin/env crystal
# encoding: utf-8
# -*- coding: utf-8 -*-
# vim:set fileencoding=utf-8:

require "json"
require "digest/md5.cr"
require "curl"

require "./config.cr"


module BRadot
  extend self
  module Video
    extend self
    
    # Download Video from API's links.
    def download(videoList : Array(String),aid : Int32|String) : Array(IO::Memory)
      result = Array(IO::Memory).new
      #headers = HTTP::Headers.new
      uri = "https://api.bilibili.com/x/web-interface/view?aid=#{aid}"    
      headers = {"User-Agent"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0","Accept"=>"*/*","Accept-Language"=>"en-US,en;q=0.5","Accept-Encoding"=>"gzip, deflate, br","Range"=>"bytes=0-","Referer"=>uri,"Origin"=>"https://www.bilibili.com","Connection"=>"keep-alive"}
      #{"User-Agent"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0","Accept"=>"*/*","Accept-Language"=>"en-US,en;q=0.5","Accept-Encoding"=>"gzip, deflate, br","Range"=>"bytes=0-","Referer"=>uri,"Origin"=>"https://www.bilibili.com","Connection"=>"keep-alive"}.each do |key,value|
      #  headers.add(key,value)
      #end
      if videoList.size == 1
        #http = HTTP::Client.get(videoList[0],headers)#`curl -# -fSL -e #{uri} -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0" -H "Accept: */*" -H "Accept-Language: en-US,en;q=0.5" -H "Accept-Encoding: gzip, deflate, br" -H "Range: bytes=0-" -H "Origin: https://www.bilibili.com" -H "Connection: keep-alive" "#{videoList[0]}"`#HTTP::Client.get(videoList[0],headers)
        curl = Curl::Easy.new(videoList[0],headers)
        #curl.headers = {"User-Agent"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0","Accept"=>"*/*","Accept-Language"=>"en-US,en;q=0.5","Accept-Encoding"=>"gzip, deflate, br","Range"=>"bytes=0-","Referer"=>uri,"Origin"=>"https://www.bilibili.com","Connection"=>"keep-alive"}
        result.push(IO::Memory.new(curl.get.body))
      else
        channel = Channel(Array(IO::Memory|Int32)).new
        thresult = Hash(Int32,IO::Memory).new
        curls = Hash(Int32,Curl::Easy).new
        videoList.each_with_index do |item,index|
          spawn do
            #http = HTTP::Client.get(item,headers)#`curl -# -fSL -e #{uri} -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0" -H "Accept: */*" -H "Accept-Language: en-US,en;q=0.5" -H "Accept-Encoding: gzip, deflate, br" -H "Range: bytes=0-" -H "Origin: https://www.bilibili.com" -H "Connection: keep-alive" "#{item}"`#HTTP::Client.get(item,headers)
            curls[index] = Curl::Easy.new(item,headers)
            #curls[index].headers = {"User-Agent"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0","Accept"=>"*/*","Accept-Language"=>"en-US,en;q=0.5","Accept-Encoding"=>"gzip, deflate, br","Range"=>"bytes=0-","Referer"=>uri,"Origin"=>"https://www.bilibili.com","Connection"=>"keep-alive"}
            channel.send([index,IO::Memory.new(curls[index].get.body)])
          end
        end
        loop do
          tmp = channel.receive
          thresult.store(tmp[0].as(Int32),tmp[1].as(IO::Memory))
          break if thresult.size >= videoList.size
        end
        pointerof(curls).clear
        thresult.keys.sort.each do |key|
          result.push(thresult[key])
        end
      end
      return result
    end

    # Get Video's Information
    # Change BRadot::SETTING["infoAll"] to true if you want the all information
    def getInfo(aid : Int32|String,p : Nil|Int32|Array(Int32) = nil) : JSON::Any
      #headers = HTTP::Headers.new
      #headers.add("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36")
      #json = JSON.parse(HTTP::Client.get("https://api.bilibili.com/x/web-interface/view?aid=#{aid}",headers).body)
      curl = Curl::Easy.new("https://api.bilibili.com",{"User-Agent"=>"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"})
      json = JSON.parse(curl.get("/x/web-interface/view?aid=#{aid}").body)
      result = Hash(String,String|JSON::Any).new
      result.store("title",json["data"]["title"])
      if p.nil?
        result.store("videoList",json["data"]["pages"].dup)
      else
        videoList = Array(JSON::Any).new
        if p.is_a?(Array)
          p.each do |item|
            videoList.push(json["data"]["pages"][item.to_i-1].dup)
          end
        else
          videoList.push(json["data"]["pages"][p.to_i-1].dup)
        end
        result.store("videoList",JSON.parse(videoList.to_json))
      end
      return JSON.parse(result.to_json)
    end

    # Get Video's Links from Private API
    def getInPrivate(aid : Int32|String,cid : Int32|String,quality : Int32|String) : Array(String)
      entropy = "rbMCKn@KuamXWlPMoJGsKcbiJKUfkPF_8dABscJntvqhRSETg"
      appkey,sec = entropy.reverse.split(//).map{|i| (i.char_at(0).ord+2).chr }.join.split(/\:/)
      uri = "https://api.bilibili.com/x/web-interface/view?aid=#{aid}"
      params = "appkey=#{appkey}&cid=#{cid}&otype=json&qn=#{quality}&quality=#{quality}&type="
      chksum = Crypto::MD5.hex_digest(params + sec)
      url = "https://interface.bilibili.com/v2/playurl?#{params}&sign=#{chksum}"
      #headers = HTTP::Headers.new
      curl = Curl::Easy.new(url,{"Referer"=>uri,"User-Agent"=>"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"})
      json = JSON.parse(curl.get.body)
      result = Array(String).new
      json["durl"].as_a.each do |item|
        result.push(item["url"].as_s)
      end
      return result
    end

    # Get Video's Links
    def getLinks(aid : Int32|String,cid : Int32|String,quality : Int32|String,sessdata : String) : Array(String)
      curl = Curl::Easy.new("https://api.bilibili.com")
      curl.headers = {"User-Agent"=>"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36","Cookie"=>"SESSDATA=#{sessdata}","Host"=>"api.bilibili.com"}
      json = JSON.parse(curl.get("/x/player/playurl?cid=#{cid}&avid=#{aid}&qn=#{quality}").body)
      result = Array(String).new
      json["data"]["durl"].as_a.each do |item|
        result.push(item["url"].as_s)
      end
      return result
    end

    # Get Video's Cover
    def getCover(aid : Int32|String) : IO::Memory|Bool
      re = /<meta data-vue-meta="true" itemprop="image" content="(?<html>.*?)">/
      headers = {"User-Agent"=>"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"}
      curl = Curl::Easy.new("https://www.bilibili.com",headers)
      link = re.match(curl.get("/video/av#{aid}").body).try &.["html"]
      unless link.nil?
        curl = Curl::Easy.new(link,headers)
        result = curl.get.body
        return IO::Memory.new(result)
      end
      return false
    end

    # Get Video's Danmaku
    def getDanmaku(cid : Int32|String) : IO::Memory
      curl = Curl::Easy.new("https://comment.bilibili.com",{"User-Agent"=>"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"})
      xml = curl.get("/#{cid}.xml").body
      return IO::Memory.new(xml)
    end

    # Get Video's Comment
    # Note : This Method Haven't Been Testted
    def getComment(aid : Int32|String,page : Int32|String = 1) : IO::Memory
      curl = Curl::Easy.new("https://api.bilibili.com")
      curl.headers = {"User-Agent"=>"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"}
      json = curl.get("/x/v2/reply?pn=#{page}&type=1&oid=#{aid}&sort=2").body
      return IO::Memory.new(json)
    end
  end
end

